package cn.codergege.myspider;

import cn.codergege.myspider.Event.Event;
import cn.codergege.myspider.Event.EventManager;
import cn.codergege.myspider.component.*;
import cn.codergege.myspider.config.Config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

/**
 * 一个爬虫应该有名字, 配置, 起始 urls
 * piplines?
 * requests?
 */
public abstract class Spider {
    protected String name;
    protected Config config;
    protected List<String> startUrls = new ArrayList<>();
    protected List<Pipline> piplines = new ArrayList<>();
    protected List<Request> requests = new ArrayList<>();

    public Spider() {
        this("未命名爬虫");
    }

    public Spider(String name) {
        this.name = name;
        EventManager.registerEvent(Event.SPIDER_STARTED, this::onStart);
    }

    public Spider startUrls(String... urls) {
        this.startUrls.addAll(Arrays.asList(urls));
        return this;
    }

    /**
     * 爬虫启动前执行
     * 需要被子类重写, 用于 client 定制
     * @param config
     * Spider 是观察者, 当 SPIDER_START 事件触发时, 回调 onStart() 方法
     */
    public void onStart(Config config) {
    }

    protected <T> Spider addPipline(Pipline<T> pipline) {
        this.piplines.add(pipline);
        return this;
    }


    /**
     * // TODO: 2019-05-31 为什么还需要 makeRequest? 根据 url 来选择不同的解析器的作用?
     * @param url
     * @param <T>
     * @return
     */
    public <T> Request<T> makeRequest(String url) {
        return makeRequest(url, this::parse); // 把一个方法当作接口传进去了?!
    }

    public <T> Request<T> makeRequest(String url, Parser<T> parser) {
        return new Request(url, parser);
    }


    /**
     * 主要职责, 解析器
     * @param response
     * @param <T>
     * @return
     */
    protected abstract <T> Result<T> parse(Response response);

    /**
     * // TODO: 2019-05-31 这个是干什么用的？
     * @param requestConsumer
     */
    protected void resetRequest(Consumer<Request> requestConsumer) {
        this.resetRequest(this.requests, requestConsumer);
    }

    protected void resetRequest(List<Request> requests, Consumer<Request> requestConsumer) {
        requests.forEach(requestConsumer::accept);
    }
}
